home *** CD-ROM | disk | FTP | other *** search
/ BBS in a Box 3 / BBS in a box - Trilogy III.iso / Files / Prog / U-Z / VideoToolBox Folder / Utilities / CalibrateLuminance / CheckContrast.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-04  |  12.2 KB  |  370 lines  |  [TEXT/KAHL]

  1. /*
  2. CheckContrast.c
  3.  
  4. CheckContrast does a rigorous testing of the contrasts produced on your monitor.
  5. It was developed in order to TEST the theory behind the ISR Video Attenuator during
  6. preparation of the Pelli & Zhang (1991) paper. It requires a 12-bit Analog to 
  7. Digital Converter (ADC). At present the code (in GetVoltage.c) assumes the Data 
  8. Translation Forerunner card in slot e.
  9.  
  10. The results including both the measured contrast errors and the theoretical error bounds
  11. are stored in a CricketGraph data file, CheckContrast.data, which may be plotted
  12. using the CricketGraph format file CheckContrast.format. The resulting figure
  13. is comparable to one in Pelli and Zhang (1991).
  14.  
  15. Getting all the errors within the bounds was tough. Everything has to work right, 
  16. including the luminance calibration and the contrast calibration done here. We now
  17. feel that the residual error is due to, in order of decreasing size: 
  18. 1. random measurement error in the contrast calibration, which can be reduced by 
  19. averaging more frames
  20. 2. drifting of the monitor gamma function between the luminance and contrast 
  21. calibrations.
  22. 3. errors of the dac (seem to be within manufacturer spec.)
  23.  
  24. You must supply a LuminanceRecord?.h, (where ? designates which screen, e.g. 1),
  25. as produced by CalibrateLuminance, for your setup, and recompile CheckContrast 
  26. to #include that file.
  27.  
  28. D.G. Pelli and L. Zhang (1991) Accurate control of contrast on microcomputer displays. 
  29. Vision Research, 31:1337-1360.
  30.  
  31. HISTORY
  32.  
  33. Lan ?        Change from RandomLetter.c to measure the real contrast display in the
  34.             testing screen by setting up a TestRect painted by pmForeColor.
  35. Lan 8/4/89    add GetVoltage from Preeti & Denis to measure the real contrast display by ForeRunner
  36. Lan 9/6/89    change contrast to Michelson contrast(set MICHELSON=1), mean luminance is set to
  37.             the mid point of luminance.
  38. 9/11/89 dgp    Increased the delay before each voltage measurement up to 40 ticks, and
  39.             then synched to monitor. This gives the photometer more time to settle, and
  40.             synchs the measurement to the display frame phase. The result is that the
  41.             calibrations are now in spec. Yay!
  42. 9/19/89 Lan Check contrast at different region of screen: divide the screen into 12
  43.             regions, namely, row 1-3, column 1-4.
  44. 10/30/89 dgp Fix bug in computation of tolerance bounds for CheckContrast.data which
  45.             would occasionally introduce a spurious bound at a discontinuity.
  46. 10/30/89 dgp Cleaned up documentation.
  47. 2/12/90    dgp    Incorporated old bug fixes 9/25/89 that by mistake were left out of this
  48.             version: Increased number of digits in printouts, to avoid contrasts becoming equal,
  49.             which confuses CricketGraph. Fixed minor bug in code that detects 
  50.             discontinuity in tolerance. Previously, a reduction in contrast ratio which
  51.             wasn't a discontinuity resulted in one of the tolerances never being 
  52.             initialized and coming out as zero. 
  53. 10/10/90 dgp Renamed the program CheckContrast from "CalibrateContrast".
  54. 10/17/90 dgp Removed unused variables.
  55. 2/16/91     dgp Now check for fpu and color quickdraw.
  56. 8/24/91    dgp    Made compatible with THINK C 5.0.
  57. 3/10/92    dgp    include mc68881.h
  58. 8/27/92    dgp    replace SysEnvirons() by Gestalt()
  59. 2/23/93    dgp    use new GDOpenWindow1 and GDDisposeWindow1.
  60. */
  61. #include "VideoToolbox.h"
  62. #include <math.h>
  63. #include "Luminance.h"
  64. double contrastRatio(double t, double L1, double L2);
  65.  
  66. #define MICHELSON    1        /* 1=use Michelson contrast, 0=use Weber contrast */
  67. #define MEASURE    1            /* 1=use ADC, 0=skip measurements to debug */
  68. #define    NCONTRAST    20        /* number of contrasts to test */
  69. #define TRSIZE        160        /* size of test patch, in pixels */
  70. #define LUMINANCE    85.0    /* background luminance in cd/m^2 */
  71. #define NFRAME        200        /* number of total frames used in VoltsDuringFrame */
  72. #define NSAMPLE        2        /* number of repetitions */
  73.                             /* NFRAME/NSAMPLE must be int. */
  74. void CheckContrast(void);
  75.  
  76. void main(void)
  77. {
  78.     Require(gestalt8BitQD);
  79.     CheckContrast();
  80. }
  81.  
  82. void CheckContrast(void)
  83. {
  84.     register short i;
  85.     int j;
  86.     Rect myRect,TestRect;
  87.     WindowPtr window = NULL,oldWindow=NULL;
  88.     double L,L1,L2;
  89.     double contrast,theContrast[NCONTRAST];
  90.     GDHandle device=NULL,oldGDHandle=NULL;
  91.     luminanceRecord LR;
  92.     static double measuredLTest[NCONTRAST],measuredLBackground[NCONTRAST],measuredContrast[NCONTRAST];
  93.     static double tolerance[NCONTRAST],LTest[NCONTRAST],LBackground[NCONTRAST];
  94.     long int finalTicks;
  95.     FILE *myfile;
  96.     double LuminancePerVolt=1000.0; /* cd/m^2 */
  97.     double e,t;
  98.     double th,tl,ch,cl,r,rh,rl;
  99.     
  100.     SetApplLimit(GetApplLimit()-50000);
  101.     GetPort(&oldWindow);
  102.     printf("\n");    /* init QuickDraw automatically */
  103.  
  104.     /* parameters of the screen calibration */    
  105.     #include "LuminanceRecord1.h"
  106.     printf("Using luminance calibration with notes:\n%s\n",LR.notes);
  107.     
  108.     /* Find device corresponding to the experimental screen. */
  109.     oldGDHandle = GetGDevice();
  110.     device = GetScreenDevice(LR.screen);
  111.     
  112.     /* Open window once, and keep open for whole experiment. */
  113.     window = GDOpenWindow1(device);
  114.     
  115.     /* create a testing Rect */
  116.     SetGDevice(device);
  117.     SetPort(window);
  118.     myRect = window->portRect;
  119.     
  120.     SetRect(&TestRect, (myRect.right+myRect.left-TRSIZE)/2, (myRect.top+myRect.bottom-TRSIZE)/2, 
  121.         (myRect.right+myRect.left+TRSIZE)/2, (myRect.top+myRect.bottom+TRSIZE)/2 );    
  122.     /*SetRect(&TestRect, (myRect.right-TRSIZE),(myRect.bottom-TRSIZE),myRect.right, myRect.bottom);    
  123.     SetRect(&TestRect, myRect.left, myRect.top,(myRect.left+TRSIZE), (myRect.top+TRSIZE));
  124.     SetRect(&TestRect, (myRect.right-TRSIZE),myRect.top,myRect.right,(myRect.top+TRSIZE));
  125.     SetRect(&TestRect, myRect.left,(myRect.bottom-TRSIZE),(myRect.left+TRSIZE), myRect.bottom);
  126.     
  127.     SetRect(&TestRect, (myRect.left+(COLUMN-1)*TRSIZE), (myRect.top+(ROW-1)*TRSIZE)
  128.                         ,(myRect.left+COLUMN*TRSIZE), (myRect.top+ROW*TRSIZE));*/
  129.  
  130.     contrast=1.0;
  131.     PmBackColor(2);            /* white background */
  132.     EraseRect(&myRect);
  133.     PmBackColor(1);
  134.     EraseRect(&TestRect);    /* test contrast */
  135.     SetPort(oldWindow);
  136.     SetGDevice(oldGDHandle);
  137.     for (i=0; i<NCONTRAST;i++) {
  138.         #if MICHELSON
  139.             L=(LR.LMax+LR.LMin)/2.0;
  140.             L=LR.LBackground;    /* use same background as in CalibrateLuminance */
  141.             L1=L*(1.0-contrast);
  142.             L2=L*(1.0+contrast);
  143.         #else
  144.             L=LUMINANCE;
  145.             if(L>LR.LMax)L=LR.LMax;
  146.             L1=(1.0-contrast)*L;
  147.             L2=L;
  148.         #endif
  149.         SetLuminance(device,&LR,2,L,L1,L2);            /* set background luminance */
  150.         tolerance[i]=SetLuminance(NULL,&LR,1,L1,L1,L2);    /* accuracy+precision */
  151.         tolerance[i]=LR.tolerance;                        /* accuracy */
  152.         SetLuminance(NULL,&LR,1,L1,L1,L2);
  153.         LTest[i]=GetLuminance(NULL,&LR,1);
  154.         SetLuminance(NULL,&LR,1,L2,L1,L2);
  155.         LBackground[i]=GetLuminance(NULL,&LR,1);
  156.         #if MICHELSON
  157.             theContrast[i]=(LBackground[i]-LTest[i])/(LBackground[i]+LTest[i]);
  158.         #else
  159.             theContrast[i]=(LBackground[i]-LTest[i])/LBackground[i];
  160.         #endif
  161.         printf("\nContrast %d, requested %.10f, nominally %.10f\n",i,contrast,theContrast[i]);
  162.         measuredLTest[i]=0.0;
  163.         measuredLBackground[i]=0.0;
  164.         
  165.         for(j=0;j<NSAMPLE;j++)    {
  166.             SetLuminance(device,&LR,1,L1,L1,L2);
  167.             #if MEASURE    
  168.                 /* measure voltage */
  169.                 if(theContrast[i]>0.1)Delay(60L,&finalTicks);
  170.                 if(theContrast[i]>0.01)Delay(6L,&finalTicks);
  171.                 LoadLuminances(device,&LR,1,2);    /* synch to monitor */
  172.                 measuredLTest[i]+=VoltsDuringFrame(NFRAME/NSAMPLE)*LuminancePerVolt/NSAMPLE;
  173.             #endif    
  174.             SetLuminance(device,&LR,1,L2,L1,L2);
  175.             #if MEASURE
  176.                 if(theContrast[i]>0.1)Delay(60L,&finalTicks);
  177.                 if(theContrast[i]>0.01)Delay(6L,&finalTicks);
  178.                 LoadLuminances(device,&LR,1,2);    /* synch to monitor */
  179.                 measuredLBackground[i]+=VoltsDuringFrame(NFRAME/NSAMPLE)*LuminancePerVolt/NSAMPLE;
  180.             #endif            
  181.         }
  182.         #if MICHELSON
  183.             measuredContrast[i]=(measuredLBackground[i]-measuredLTest[i])/(measuredLBackground[i]+measuredLTest[i]);
  184.             e=tolerance[i]/(measuredLBackground[i]+measuredLTest[i]);
  185.         #else
  186.             measuredContrast[i]=(measuredLBackground[i]-measuredLTest[i])/measuredLBackground[i];
  187.             e=tolerance[i]/measuredLBackground[i];
  188.         #endif
  189.         printf("LTest %6.2f %6.2f LBackground %6.2f %6.2f tolerance %6.2f\n",
  190.             LTest[i],measuredLTest[i],LBackground[i],measuredLBackground[i],tolerance[i]);
  191.         printf("contrast %5.5f %5.5f",theContrast[i],measuredContrast[i]);
  192.         printf(" error/tolerance %10.5f\n",(measuredContrast[i]-theContrast[i])/e);
  193.         contrast /= sqrt(2.0);
  194.         FlushEvents(everyEvent,0);
  195.     }
  196.     SetPort(oldWindow);
  197.     SetGDevice(oldGDHandle);
  198.     printf("\007Measurements are done. Now saving results to disk\n");
  199.     /* open and write into file */
  200.     myfile=fopen("CheckContrast.data","w");
  201.     SetFileInfo("CheckContrast.data",'TEXT','CGRF');
  202.     fprintf(myfile,"*\n");
  203.     fprintf(myfile,
  204.         "C\tmC\tmC-C"
  205.         "\t+e"
  206.         "\t-e"
  207.         "\t|mC-C|/C"
  208.         "\te/C"
  209.         "\terror/tolerance"
  210.         "\tmC/C"
  211.         "\t(C+e)/C"
  212.         "\t(C-e)/C"
  213.         "\tLTest\tmLTest\tmLTest-LTest"
  214.         "\tLBackground\tmLBackground\tmLBackground-LBackground"
  215.         "\ttolerance\n");
  216.     for(i=0;i<NCONTRAST;i++){
  217.         /* after first iteration, compute contrast ratio errors */
  218.         if(i>0) {
  219.             rl=contrastRatio(tolerance[i-1],LTest[i-1],LBackground[i-1]);
  220.             rh=contrastRatio(tolerance[i],LTest[i],LBackground[i]);
  221.         }
  222.         /* if there's a discontinuity in tolerance, then insert extra pair of records */
  223.         if(i>0 && rh<rl){
  224.             /* find discontinuity */
  225.             cl=theContrast[i-1];
  226.             ch=theContrast[i];
  227.             tl=tolerance[i-1];
  228.             th=tolerance[i];
  229.             for(j=0;j<15;j++){
  230.                 if((cl-ch)<0.0001) break;    /* Cricket Graph gets confused if (cl-ch) is too small */
  231.                 contrast=(cl+ch)/2.0;
  232.                 #if MICHELSON
  233.                     L=(LR.LMax+LR.LMin)/2.0;
  234.                     L=LR.LBackground;    /* use same background as in CalibrateLuminance */
  235.                     L1=L*(1.0-contrast);
  236.                     L2=L*(1.0+contrast);
  237.                 #else
  238.                     L=LUMINANCE;
  239.                     if(L>LR.LMax)L=LR.LMax;
  240.                     L1=(1.0-contrast)*L;
  241.                     L2=L;
  242.                 #endif
  243.                 t=SetLuminance(NULL,&LR,1,L1,L1,L2);    /* accuracy+precision */
  244.                 t=LR.tolerance;                            /* accuracy */
  245.                 r=contrastRatio(t,L1,L2);
  246.                 if(r>=rl){
  247.                     rl=r;
  248.                     tl=t;
  249.                     cl=contrast;
  250.                 }
  251.                 else {
  252.                     rh=r;
  253.                     th=t;
  254.                     ch=contrast;
  255.                 }
  256.             }
  257.             /* make sure there IS a discontinuity */
  258.             if(rh*1.001<rl){
  259.                 /* print out one record at lower bound w/o data */
  260.                 t=tl;
  261.                 contrast=cl;
  262.                 #if MICHELSON
  263.                     e=t/(L1+L2);
  264.                 #else
  265.                     e=t/L2;
  266.                 #endif
  267.                 #if MICHELSON
  268.                     L=(LR.LMax+LR.LMin)/2.0;
  269.                     L=LR.LBackground;    /* use same background as in CalibrateLuminance */
  270.                     L1=L*(1.0-contrast);
  271.                     L2=L*(1.0+contrast);
  272.                 #else
  273.                     L=LUMINANCE;
  274.                     if(L>LR.LMax)L=LR.LMax;
  275.                     L1=(1.0-contrast)*L;
  276.                     L2=L;
  277.                 #endif
  278.                 fprintf(myfile,"%.10f\t\t\t%.10f\t%.10f\t"
  279.                     ,contrast
  280.                     ,e
  281.                     ,-e
  282.                 );
  283.                 fprintf(myfile,"\t%.10f\t\t\t%.10f\t%.10f\t%.10f\t\t\t%.10f\t\t\t%.10f\n"
  284.                     ,e/contrast
  285.                     ,(contrast+e)/contrast
  286.                     ,(contrast-e)/contrast
  287.                     ,L1
  288.                     ,L2
  289.                     ,t
  290.                 );
  291.                 /* print out one record at upper bound w/o data */
  292.                 t=th;
  293.                 contrast=ch;
  294.                 #if MICHELSON
  295.                     e=t/(L1+L2);
  296.                 #else
  297.                     e=t/L2;
  298.                 #endif
  299.                 #if MICHELSON
  300.                     L=(LR.LMax+LR.LMin)/2.0;
  301.                     L=LR.LBackground;    /* use same background as in CalibrateLuminance */
  302.                     L1=L*(1.0-contrast);
  303.                     L2=L*(1.0+contrast);
  304.                 #else
  305.                     L=LUMINANCE;
  306.                     if(L>LR.LMax)L=LR.LMax;
  307.                     L1=(1.0-contrast)*L;
  308.                     L2=L;
  309.                 #endif
  310.                 fprintf(myfile,"%.10f\t\t\t%.10f\t%.10f\t"
  311.                     ,contrast
  312.                     ,e
  313.                     ,-e
  314.                 );
  315.                 fprintf(myfile,"\t%.10f\t\t\t%.10f\t%.10f\t%.10f\t\t\t%.10f\t\t\t%.10f\n"
  316.                     ,e/contrast
  317.                     ,(contrast+e)/contrast
  318.                     ,(contrast-e)/contrast
  319.                     ,L1
  320.                     ,L2
  321.                     ,t
  322.                 );
  323.             }
  324.         }
  325.         /* print out one record of data */
  326.         #if MICHELSON
  327.             e=tolerance[i]/(LBackground[i]+LTest[i]);
  328.         #else
  329.             e=tolerance[i]/LBackground[i];
  330.         #endif
  331.         fprintf(myfile,"%.10f\t%.10f\t%.10f\t%.10f\t%.10f\t"
  332.             ,theContrast[i]
  333.             ,measuredContrast[i]
  334.             ,measuredContrast[i]-theContrast[i]
  335.             ,e
  336.             ,-e
  337.         );
  338.         fprintf(myfile,"%.10f\t%.10f\t%.10f\t%.10f\t%.10f\t%.10f\t%.10f\t%.10f\t%.10f\t%.10f\t%.10f\t%.10f\t%.10f\n"
  339.             ,fabs(measuredContrast[i]-theContrast[i])/theContrast[i]
  340.             ,e/theContrast[i]
  341.             ,(measuredContrast[i]-theContrast[i])/e
  342.             ,measuredContrast[i]/theContrast[i]
  343.             ,(theContrast[i]+e)/theContrast[i]
  344.             ,(theContrast[i]-e)/theContrast[i]
  345.             ,LTest[i]
  346.             ,measuredLTest[i]
  347.             ,measuredLTest[i]-LTest[i]
  348.             ,LBackground[i]
  349.             ,measuredLBackground[i]
  350.             ,measuredLBackground[i]-LBackground[i]
  351.             ,tolerance[i]
  352.         );
  353.     }
  354.     fclose(myfile);
  355.     SetGDevice(oldGDHandle);
  356.     GDDisposeWindow1(window);
  357. }
  358.  
  359. double contrastRatio(double t, double L1, double L2)
  360. {
  361. double e,contrast;
  362.         #if MICHELSON
  363.             e=t/(L1+L2);
  364.             contrast=fabs(L1-L2)/(L1+L2);
  365.         #else
  366.             e=t/L2;
  367.             contrast=(L2-L1)/L2;
  368.         #endif
  369.         return (contrast+e)/contrast;
  370. }